// File: mLIBVec2DTests.js
//
// Used for testing the mLIBVec2D library
// it acts as an environment loader.
//
// Usage: $js -f mLIBVec2DTests.js


load('../mLibFactory.js', '../mLibIncludes.js');
load('../mLibVec2D.js');
load('../mLibVec3D.js');
load('../mLibVec4D.js');
load('../mLibLineSegment.js');
load('../mLibLin3.js');
load('../mLibRay3.js');
load('../mLibPlane.js');
load('../mLibTriangle.js');

var testFrame = mLIB;
testFrame.debugON = false;
load('aasinchu.js');
expect = mLIB.expect;

var sqrt = mLIB.Includes.sqrt;
    isZero = mLIB.Includes.isZero;
function dot(u, v) { return u.x*v.x + u.y*v.y + u.z*v.z; };
function distance(p0, p1) {
    var dx = p0.x-p1.x,
        dy = p0.y-p1.y,
        dz = p0.z-p1.z;
    return sqrt(dx*dx + dy*dy + dz*dz);
}
function cross(U, V) {
    return {
        x: U.y*V.z-U.z*V.y,
        y: U.z*V.x-U.x*V.z,
        z: U.x*V.y-U.y*V.x
    };
}



//testSuiteVec3();
//testSuiteVec2();
//testSuiteLin3();
//testSuiteRay3Instantion();
//testSuiteRay3();
//testSuiteLin3Seg();
//testSuiteLin3SegPart2();
//testSuiteLin3SegClosestPoints();
//testSuiteLin3SegDistanceSquared();
//testSuitePlane();
testSuiteTriangle();

// testing aasinchu itself
var testSuiteAasinchu = function() {
	performTestSuite('Testing aasinchu', function() {

		testFor('Checking for property [length]', function() {
			var v = mLIB.Vec2(3.0, 4.0);

			expect(v).to.contain.property('length');
		});
		
		testFor('Checking sub-prop', function() {
			var set = {a:{b:1, c:2}, d:3},
				sub = {b:1, c:2};
			expect(set).to.equal({d:3, a:sub});
		});
	});
};  //testSuiteAasinchu

//testSuiteAasinchu();

function testSuiteTriangle() {
	performTestSuite('Triangle testing', function() {
		testFor('Triangle 2D: isPointInTriangle', function() {
            var P0 = mLIB.Point2(0, 2),
                P1 = mLIB.Point2(0, 0),
                P2 = mLIB.Point2(2, 0);
                
            expect(mLIB.Triangle.isPointInTriangle2D(mLIB.Point2(1,1), P0, P1, P2) ).to.be.ok();
            expect(mLIB.Triangle.isPointInTriangle2D(mLIB.Point2(3,1), P0, P1, P2) ).to.not.be.ok();
            expect(mLIB.Triangle.isPointInTriangle2D(mLIB.Point2(2,0), P0, P1, P2) ).to.be.ok;
        });
        
		testFor('Triangle 3D: isPointInTriangle', function() {
            var P0 = mLIB.Point3(0, 2, 0),  // triangle in the xy plane
                P1 = mLIB.Point3(0, 0, 0),
                P2 = mLIB.Point3(2, 0, 0);
                
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(1,1,0), P0, P1, P2) ).to.be.ok();
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(3,1,0), P0, P1, P2) ).to.not.be.ok();
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(2,0,0), P0, P1, P2) ).to.be.ok();

            // general triangle
            P0 = mLIB.Point3(1, 2, 0);
            P1 = mLIB.Point3(12, 2, 0);
            P2 = mLIB.Point3(8, 2, 10);

            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(6,2,3), P0, P1, P2) ).to.be.ok();
            
            // point is not on the triangle plane, but is in the coplanar projection, hence ok
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(3,1,1), P0, P1, P2) ).to.be.ok();
            
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(8,2,10), P0, P1, P2) ).to.be.ok();
        });
        
 		testFor('Triangle 3D: baryCentric coords', function() {
            var P0 = mLIB.Point3(0, 2, 0),  // triangle in the xy plane
                P1 = mLIB.Point3(0, 0, 0),
                P2 = mLIB.Point3(2, 0, 0),
                u = P1.Minus(P0),
                v = P2.Minus(P0),
                Q, su, tv, bc;
            
            // test 1
            Q = mLIB.Point3(1,1,0);
            bc = mLIB.Triangle.baryCentricCoords(Q, P0, P1, P2);
            su = u.ScaleUp(bc.s); tv = v.ScaleUp(bc.t);
            Pst = P0.Plus(su.plus(tv)); // parametric plane equation: P(s, t) = P0 + su + tv
            expect(Pst).to.be.equal(Q);
            
            // twst 2
            Q = mLIB.Point3(1,0.5,0);
            bc = mLIB.Triangle.baryCentricCoords(Q, P0, P1, P2);
            su = u.ScaleUp(bc.s); tv = v.ScaleUp(bc.t);
            Pst = P0.Plus(su.plus(tv));
            expect(Pst).to.be.equal(Q);
            
            // test 3
            Q = mLIB.Point3(1,3,0);
            bc = mLIB.Triangle.baryCentricCoords(Q, P0, P1, P2);
            su = u.ScaleUp(bc.s); tv = v.ScaleUp(bc.t);
            Pst = P0.Plus(su.plus(tv));
            if (mLIB.Triangle.isPointInTriangle(Q, P0, P1, P2)) expect(Pst).to.be.equal(Q)
            else expect(Pst).to.not.be.equal(Q);

/* TBD
            Q = mLIB.Point3(1,1,1); // point off the triangle plane *(xy)
            bc = mLIB.Triangle.baryCentricCoords(Q, P0, P1, P2);
            su = u.ScaleUp(bc.s); tv = v.ScaleUp(bc.t);
            Pst = P0.Plus(su.plus(tv));
            
console.log('bc = ', bc.s, bc.t);
console.log(Q.log(), Pst.log(), mLIB.Triangle.isPointInTriangle(Q, P0, P1, P2));

            
            // general triangle
            P0 = mLIB.Point3(1, 2, 0);
            P1 = mLIB.Point3(12, 2, 0);
            P2 = mLIB.Point3(8, 2, 10);

            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(6,2,3), P0, P1, P2) ).to.be.ok();
            
            // point is not on the triangle plane, but is in the coplanar projection, hence ok
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(3,1,1), P0, P1, P2) ).to.be.ok();
            
            expect(mLIB.Triangle.isPointInTriangle(mLIB.Point3(8,2,10), P0, P1, P2) ).to.be.ok();
*/
        });
        
 		testFor('Triangle 2D: intersecting triangles: triangleIntersect2D', function() {
            var P0 = mLIB.Point2(0, 2),  // triangle in the xy plane
                P1 = mLIB.Point2(0, 0),
                P2 = mLIB.Point2(2, 0),
                
                Q0 = mLIB.Point2(0, 1), 
                Q1 = mLIB.Point2(0, 3),
                Q2 = mLIB.Point2(2, 3);
                
            expect(mLIB.Triangle.triangleIntersect2D(P0, P1, P2, Q0, Q1, Q2) ).to.be.ok();
            
            Q0 = mLIB.Point2(0, 5);
            expect(mLIB.Triangle.triangleIntersect2D(P0, P1, P2, Q0, Q1, Q2) ).to.not.be.ok();

            Q0 = mLIB.Point2(1, .5);
            expect(mLIB.Triangle.triangleIntersect2D(P0, P1, P2, Q0, Q1, Q2) ).to.be.ok();

            
        });
        
        testFor('Triangle 3D: intersecting triangles: triangleIntersect3D', function() {
            /*
            var P0 = mLIB.Point3(30, 40, 0),
                P1 = mLIB.Point3(30, 20, 40),
                P2 = mLIB.Point3(30, 0, 0),
                
                ray = mLIB.Ray3(
                    mLIB.Point3(10, 10, 10),
                    mLIB.Point3(40, 10, 10)
                ),
                lp = {},

                Q0 = mLIB.Point3(20, 20, 10),
                Q1 = mLIB.Point3(60, 0, 10),
                Q2 = mLIB.Point3(60, 30, 10);
                
                console.log('P orientation:');
                var n = P1.Minus(P0).cross(P2.Minus(P0));
                console.log('    P nornal = ', n.log());
                var n = Q1.Minus(Q0).cross(Q2.Minus(Q0));
                console.log('    Q nornal = ', n.log());
                
                
///////
            
            function det(a, b, c, d) {
               // (a-d).[(b-d)x(c-d)]
               return ( a.Minus(d).dot( (b.Minus(d)).cross(c.Minus(d)) ) );
            }
            
            console.log('P normal: ', (P1.Minus(P0)).cross(P2.Minus(P0)).log() );
            console.log('Q normal: ', (Q1.Minus(Q0)).cross(Q2.Minus(Q0)).log() );
            

            console.log('[ P0, Q1, Q2, Q0 ] = ', det(P0, Q1, Q2, Q0) ); // (q2, q1, p2, p1) );
            console.log('[ P1, Q1, Q2, Q0 ] = ', det(P1, Q1, Q2, Q0) ); // (q2, q1, p2, p1) );
            console.log('[ P2, Q1, Q2, Q0 ] = ', det(P2, Q1, Q2, Q0) ); // (q2, q1, p2, p1) );

            //      [ P0, Q1, Q2, Q0 ]          == [ P0, Q0, Q1, Q2 ];
            // ie.  (P0-Q0).( (Q1-Q0)x(Q2-Q0) ) == (P0-Q2).( (Q0-Q2)x(Q1-Q2) )
            
            
            if (mLIB.Triangle.triangleIntersect(P0, P1, P2, Q0, Q1, Q2)) {
                        console.log('COLLISION: TRiangle/Triangle!!');
            }
            if (mLIB.Triangle.triangleIntersectRay(P0, P1, P2, ray, lp)) {
                        console.log('COLLISION: Ray/Triangle!!');
            }
            */
/////
    var rp0,rp1,rp2, gp0,gp1,gp2, ray, lp = {};

    // red should intersect but test is incorrect -------------------
    rp0 = mLIB.Point3(-79.22867273082738,-7.3004534809614885,-7.617942193387172);
    rp1 = mLIB.Point3(96.51337145231267,-69.77402267740725,49.908242029402075);
    rp2 = mLIB.Point3(85.48376516405983,90.87314673295612,50.895800600604154);
    
    gp0 = mLIB.Point3(-8.282065589212195,-5.043762106700134,78.22139892798612);
    gp1 = mLIB.Point3(-71.65722211691865,38.0442955490402,-78.21654197324848);
    gp2 = mLIB.Point3(20.22657595516898,41.02419918192194,4.376894873897985);
    
    ray = mLIB.Ray3( mLIB.Point3(-50,-50,-50), mLIB.Vec3(0.3997963154787681, 0.23429583933799694, 0.8861536919736354) );



// should intersecrt green
console.log('RAY/TRI COLLIS TEST:');
rp0 = mLIB.Point3(-23.026031992876717,-19.634699031326086,26.087686641774066);
rp1 = mLIB.Point3(22.464336134933816,-31.942853451554466,97.37024243378082);
rp2 = mLIB.Point3(-23.903660235394884,77.33844902710337,96.46239642280291);
gp0 = mLIB.Point3(24.509355510303276,17.359853121760604,-92.69643907530055);
gp1 = mLIB.Point3(-38.324329239996445,-63.08277453461976,9.281724988010353);
gp2 = mLIB.Point3(-30.79617678682649,-0.5441748606290875,40.08330300922282);
ray = mLIB.Ray3( mLIB.Point3(-50,-50,-50), mLIB.Vec3(0.9490709245619535,0.31398690740291413,0.026011576858636535) );

    if (mLIB.Triangle.triangleIntersectRay_old(rp0, rp1, rp2, ray)) {
                console.log('test1a COLLISION: Ray intersects red Triangle!!');
    }

    if (mLIB.Triangle.triangleIntersectRay_old(gp0, gp1, gp2, ray, lp)) {
        console.log('old COLLISION: Ray intersects green Triangle!!');
    }




//////////////////////

        });
  
        
    }); // performTestSuite()
}; // testSuiteTriangle()


function testSuitePlane() {
	performTestSuite('Plane testing', function() {
		testFor('Plane instantiation', function() {
                
            // create the xy plane
            var P = mLIB.Point3(0,0,0), Q = mLIB.Point3(1,0,0), R = mLIB.Point3(0,1,0),
                v1 = P.Minus(Q), v2 = R.Minus(P);

            plane = mLIB.Plane(P, Q, R);
            expect(plane).to.be.equal({nNormal:{x:0,y:0,z:1}, dOffset:0.0});
            expect(isZero(plane.nNormal.dot(v1))).to.be.ok()
            expect(isZero(plane.nNormal.dot(v2))).to.be.ok()
            
            // test2 - xy
            P = mLIB.Point3(0,1,0); Q = mLIB.Point3(1,0,0); R = mLIB.Point3(1,1,0);
            v1 = P.Minus(Q); v2 = R.Minus(P);

            plane = mLIB.Plane(P, Q, R);
            expect(plane).to.be.equal({nNormal:{x:0,y:0,z:1}, dOffset:0.0});
            expect(isZero(plane.nNormal.dot(v1))).to.be.ok()
            expect(isZero(plane.nNormal.dot(v2))).to.be.ok()
                       
            // test3 - xz
            P = mLIB.Point3(0,0,0); Q = mLIB.Point3(1,0,0); R = mLIB.Point3(0,0,1);
            v1 = P.Minus(Q); v2 = R.Minus(P);

            plane = mLIB.Plane(P, Q, R);
            expect(plane).to.be.equal({nNormal:{x:0,y:-1,z:0}, dOffset:0.0});
            expect(isZero(plane.nNormal.dot(v1))).to.be.ok()
            expect(isZero(plane.nNormal.dot(v2))).to.be.ok()

            //test4 
            P = mLIB.Point3(1,0,0); Q = mLIB.Point3(0,1,0); R = mLIB.Point3(.5,.5,1);
            v1 = P.Minus(Q); v2 = R.Minus(P);

            plane = mLIB.Plane(P, Q, R);
            expect(isZero(plane.nNormal.dot(v1))).to.be.ok()
            expect(isZero(plane.nNormal.dot(v2))).to.be.ok()
           
            // create using a,b,c,d
            var pNormal = mLIB.Vec3(0.7071067811865476, 0.7071067811865476, 0 ),
                delta1, delta2;
            plane = mLIB.Plane(0.7071067811865476, 0.7071067811865476, 0, -0.7071067811865476);
            delta1 = 1.0 - pNormal.dot(plane.nNormal);
            delta2 = plane.dOffset + 0.7071067811865476;
            
            expect(isZero(delta1)).to.be.ok();
            expect(isZero(delta2)).to.be.ok();
            
        });

		testFor('Plane ClosestPoint', function() {
            // create the xy plane
            var P = mLIB.Point3(0,0,0), Q = mLIB.Point3(1,0,0), R = mLIB.Point3(0,1,0),
                v1 = P.Minus(Q), v2 = R.Minus(P),
                testP = mLIB.Point3(3,3,3);

            var plane = mLIB.Plane(P, Q, R);
            expect(mLIB.Plane(P, Q, R).ClosestPoint(testP)).to.be.equal({x:3, y:3, z:0});

        }); 

		testFor('Plane: point distance test', function() {
            // create the xy plane
            var P = mLIB.Point3(0,0,0), Q = mLIB.Point3(1,0,0), R = mLIB.Point3(0,1,0),
                v1 = P.Minus(Q), v2 = R.Minus(P),
                testP = mLIB.Point3(3,3,3);

            var plane = mLIB.Plane(P, Q, R);
            expect(mLIB.Plane(P, Q, R).distance(testP)).to.be.eq(3);

        });
        
		testFor('Plane: point orientation test', function() {
            // create the xy plane
            var P = mLIB.Point3(0,0,0), Q = mLIB.Point3(1,0,0), R = mLIB.Point3(0,1,0),
                v1 = P.Minus(Q), v2 = R.Minus(P),
                plane = mLIB.Plane(P, Q, R),
                testP;
            
            // test1
            testP = mLIB.Point3(3,3,3);
            expect(mLIB.Plane(P, Q, R).test(testP)).to.be.gt(0);
            
            // test2
            testP = mLIB.Point3(-3,-3,3);
            expect(mLIB.Plane(P, Q, R).test(testP)).to.be.gt(0);
            
            // test2
            testP = mLIB.Point3(3,3,-3);
            expect(mLIB.Plane(P, Q, R).test(testP)).to.be.lt(0);
            

        }); 


    }); // performTestSuite()

};  //testSuitePlane()


function testSuiteLin3Seg() {
	performTestSuite('Inital tests for mLIB.Lin3Seg', function() {
		testFor('Lin3Seg instantiation 1. using start/end points', function() {
            var p0 = mLIB.Point3(3.0, 4.0, 5.0),
				p1 = mLIB.Point3(5.0, 4.0, 5.0),
				dir = p1.Minus(p0);
				
            expect(mLIB.Lin3Seg(p0,p1)).to.be.equal({origin:p0, direction:dir});
        });

		testFor('Lin3Seg instantiation 2. using existing Lin3Seg', function() {
            var p0 = mLIB.Point3(3.0, 4.0, 5.0),
				p1 = mLIB.Point3(5.0, 4.0, 5.0),
				seg1 = mLIB.Lin3Seg(p0,p1),
				dir = p1.Minus(p0);
				
            expect(mLIB.Lin3Seg(seg1)).to.be.equal({origin:p0, direction:dir});
        });

		testFor('Lin3Seg instantiation 3. get default set', function() {
            var origin = mLIB.Point3.origin;
				dir = mLIB.Vec3.xAxis;
				
            expect(mLIB.Lin3Seg()).to.be.equal({origin:origin, direction:dir});
        });
		
		testFor('Lin3Seg testing for equality', function() {
            var p0 = mLIB.Point3(3.0, 4.0, 5.0),
				p1 = mLIB.Point3(5.0, 4.0, 5.0),
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(seg1);
				
            expect(seg1.equalTo(seg2)).to.be.ok();
        });

		testFor('Lin3Seg testing for not equality', function() {
            var p0 = mLIB.Point3(3.0, 4.0, 5.0),
				p1 = mLIB.Point3(5.0, 4.0, 5.0),
				p2 = mLIB.Point3(5.0, 4.0, 6.0),
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(p0, p2);
				
            expect(seg1.notEqualTo(seg2)).to.be.ok();
        });

		testFor('Lin3Seg testing ClosestPoint on segment to a given point Q', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				Q = mLIB.Point3(2.0, 4.0, 0.0),
				seg = mLIB.Lin3Seg(p0, p1);
				
            expect(seg.ClosestPoint(Q)).to.be.equal({x:2, y:0, z:0});
        });
		
		testFor('Lin3Seg testing ClosestPoints between 2 line segments a)', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				q0 = mLIB.Point3(2.0, 4.0, 0.0),
				q1 = mLIB.Point3(8.0, 3.0, 0.0);
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(q0, q1),
				points = seg1.ClosestPoints(seg2),
				n = points.Pc.Minus(points.Qc);

			expect(points.Pc).to.be.equal(p1);
			expect(mLIB.Includes.isZero(n.dot(seg2.direction))).to.be.ok();
 
        });

		testFor('Lin3Seg testing ClosestPoints between 2 line segments b)', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				q0 = mLIB.Point3(0.0, -2.0, 3.0),
				q1 = mLIB.Point3(4.0, 4.0, 3.0);
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(q0, q1),
				points = seg1.ClosestPoints(seg2),
				n = points.Pc.Minus(points.Qc);

			expect(mLIB.Includes.isZero(n.dot(seg1.direction))).to.be.ok();
			expect(mLIB.Includes.isZero(n.dot(seg2.direction))).to.be.ok();
        });
		
		testFor('Lin3Seg testing ClosestPoints between 2 line segments c)', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 4.0, 3.0),
				q0 = mLIB.Point3(12.0, -2.0, 3.0),
				q1 = mLIB.Point3(4.0, 4.0, -7.0);
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(q0, q1),
				points = seg1.ClosestPoints(seg2),
				n = points.Pc.Minus(points.Qc);

			expect(mLIB.Includes.isZero(n.dot(seg1.direction))).to.be.ok();
			expect(mLIB.Includes.isZero(n.dot(seg2.direction))).to.be.ok();
        });
		
		testFor('Lin3Seg testing shortest distanceSquared from segment to a given point Q', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				Q = mLIB.Point3(8.0, 4.0, 0.0),
				seg = mLIB.Lin3Seg(p0, p1);
				
            expect(seg.distanceSquared(Q)).to.be.eq(25.0);
        });

		testFor('Lin3Seg testing shortest distanceSquared between 2 line segments a)', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				q0 = mLIB.Point3(2.0, 4.0, 0.0),
				q1 = mLIB.Point3(5.0, 3.0, 0.0);
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(q0, q1);
				
			expect(seg1.distanceSquared(seg2)).to.be.eq(9.0);

		});

		testFor('Lin3Seg testing shortest distanceSquared between 2 line segments b)', function() {
            var p0 = mLIB.Point3.origin,
				p1 = mLIB.Point3(5.0, 0.0, 0.0),
				q0 = mLIB.Point3(2.0, 4.0, 0.0),
				q1 = mLIB.Point3(-3.0, 2.0, 0.0);
				seg1 = mLIB.Lin3Seg(p0, p1),
				seg2 = mLIB.Lin3Seg(q0, q1),
				points = seg1.ClosestPoints(seg2);
		
			expect(seg1.distanceSquared(seg2)).to.be.eq(mLIB.Point3.distanceSquared(points.Pc, points.Qc));

		});

	});  //performTestSuite

}; //testSuiteLin3Seg

function testSuiteLin3SegClosestPoints() {
	performTestSuite('Lin3Seg testing closestPoints', function() {
        testFor('Lin3Seg closest points between a segment and a line', function() {
			var seg = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray

				line,
				points,
				n;

            // test 0 parallell seg n line
            line = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = seg.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);       
            expect(mLIB.Includes.isZero(n.dot(seg.direction))).to.be.ok();
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 1
            line = mLIB.Lin3( Q1, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 2
            line = mLIB.Lin3( Q2, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 3
            line = mLIB.Lin3( Q3, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

        });
        
        testFor('Lin3Seg closest points between a segment and a ray', function() {
			var seg = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray

				line,
				points,
				n;

            // test 0 parallell seg n line
            ray = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = seg.ClosestPoints(ray);
            n = points.Pc.Minus(points.Qc);       
            expect(mLIB.Includes.isZero(n.dot(seg.direction))).to.be.ok();
            expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

            // test 1
            ray = mLIB.Lin3( Q1, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

            // test 2
            ray = mLIB.Lin3( Q2, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            n = points.Pc.Minus(points.Qc);
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            
            expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

            // test 3
            ray = mLIB.Lin3( Q3, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(seg.direction))) {
                expect(points.Pc).to.equal(seg.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

        });

    }); //performTestSuite()

}; // testSuiteLin3SegClosestPoints()


function testSuiteLin3SegDistanceSquared() {

	performTestSuite('Lin3Seg testing distanceSquared', function() {
        testFor('Lin3Seg distanceSquared between a segment and a line', function() {
			var seg = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				line,
				points,
				nsq, dsq;
                
            // test 0 parallel rays
            line = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = seg.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();
            
            // test 1
            line = mLIB.Ray3( Q1, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 2
            line = mLIB.Ray3( Q2, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 3
            line = mLIB.Ray3( Q3, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();
        });

        testFor('Lin3Seg distanceSquared between a segment and a ray', function() {
			var seg = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				ray,
				points,
				nsq, dsq;
                
            // test 0 parallel rays
            ray = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = seg.ClosestPoints(ray);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(ray);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();
            
            // test 1
            ray = mLIB.Ray3( Q1, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(ray);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 2
            ray = mLIB.Ray3( Q2, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(ray);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 3
            ray = mLIB.Ray3( Q3, mLIB.Vec3(12, -43, -30) );
            points = seg.ClosestPoints(ray);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = seg.distanceSquared(ray);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

        });


    });  // performTestSuite()


} // testSuiteLin3SegDistanceSquared()


//----------------------------------------------------------------------

function testSuiteLin3() {
	performTestSuite('Inital tests for mLIB.Lin3', function() {
        testFor('Lin3 instantiaion, with origin and direction vector', function() {
			var pO = mLIB.Point3(0, 0, 0),
				dir = mLIB.Vec3(1, 1, 1),
				line = mLIB.Lin3(pO, dir);
		
            expect(line).to.be.equal({origin:{x:0.0, y:0.0, z:0.0}, direction: dir.Normalise()});
        });

        testFor('Lin3 instantiaion, with Lin3Seg', function() {
			var pO = mLIB.Point3(0, 0, 0),
				dir = mLIB.Vec3(1, 1, 1),
				seg = mLIB.Lin3Seg(pO, dir),
				line = mLIB.Lin3(seg);
		
            expect(line).to.be.equal({origin:{x:0.0, y:0.0, z:0.0}, direction: dir.Normalise()});
        });

        testFor('Lin3 instantiaion, with Lin3', function() {
			var pO = mLIB.Point3(0, 0, 0),
				dir = mLIB.Vec3(1, 1, 1),
				seg = mLIB.Lin3Seg(pO, dir),
				line1 = mLIB.Lin3(seg),
				line2 = mLIB.Lin3(line1);
		
            expect(line2).to.be.equal(line1).and.not.sameAs(line1);
        });

        testFor('Lin3 , equalTo/notEqualTo', function() {
			var pO = mLIB.Point3(0, 0, 0),
				dir0 = mLIB.Vec3(1, 1, 1),
				dir1 = mLIB.Vec3(1,2,3),
				qO = mLIB.Point3(2,3,4),
				seg = mLIB.Lin3Seg(pO, dir0),
				line1 = mLIB.Lin3(seg),
				line2 = mLIB.Lin3(line1),
				line3 = mLIB.Lin3(qO, dir1);
		
            expect(line1.equalTo(line2)).to.be.ok();
            expect(line1.notEqualTo(line2)).to.not.be.ok();
			
			expect(line1.equalTo(line3)).to.not.be.ok();
			expect(line1.notEqualTo(line3)).to.be.ok();
			
        });

        testFor('Lin3 , ClosestPoint on line to a point Q', function() {
			var pO = mLIB.Point3(5, 23, 47),
				dir0 = mLIB.Vec3(12, 43, 30),
				line = mLIB.Lin3(pO, dir0),
				cp, pv, tp;


			tp = mLIB.Point3(0,0,0);
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);

			tp = mLIB.Point3(-1,-1,-1);
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);

			tp = mLIB.Point3(401, 29.7,305.5);
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);
			
			tp = mLIB.Point3(1,1,0);
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);

			tp = mLIB.Point3(1,1,0);
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);

			tp = mLIB.Point3(1,1,66)
			cp = line.ClosestPoint(tp);
			pv = mLIB.Vec3(cp, tp);
			expect(mLIB.Includes.roundToZero(pv.dot(dir0))).to.be.eq(0);

        });
		
        testFor('Lin3 , ClosestPoints between 2 lines', function() {
			var dir0 = mLIB.Vec3(12, 43, 30), line1 = mLIB.Lin3( mLIB.Point3(5, 23, 47), dir0 ),
				dir1 = mLIB.Vec3(23, 11, 4),  line2 = mLIB.Lin3( mLIB.Point3(12, 7, 23), dir1),
				points = line1.ClosestPoints(line2),
				nc = mLIB.Vec3(points.Pc, points.Qc); // points.Pc.Minus(points.Qc);
				
			expect(mLIB.Includes.roundToZero(nc.dot(dir0))).to.be.eq(0);
			expect(mLIB.Includes.roundToZero(nc.dot(dir1))).to.be.eq(0);


		});

        testFor('Lin3 , distanceSquared between line/point & line/line', function() {
			var line1 = mLIB.Lin3( mLIB.Point3(5, 23, 47), mLIB.Vec3(12, 43, 30) ),
				p = mLIB.Point3(12, -23, 44),
				dsq = line1.distanceSquared(p),
				q = line1.ClosestPoint(p),
				pvsq = mLIB.Vec3(q, p).lengthSquared();

			expect(mLIB.Includes.roundToZero( dsq - pvsq )).to.be.eq(0);
			
			var dir = mLIB.Vec3(12, 43, 30),
				line2 = mLIB.Lin3( mLIB.Point3(12, 3, 56), dir),
				points = line1.ClosestPoints(line2),
				ncsq = points.Pc.Minus(points.Qc).lengthSquared();
				
				dsq = line1.distanceSquared(line2);
				expect(mLIB.Includes.roundToZero( dsq - ncsq )).to.be.eq(0);
						
		});
		
	});  //performTestSuite

}; //testSuiteLin3

function testSuiteRay3Instantion() {
	performTestSuite('mLIB.Ray3 Instantions', function() {

        testFor('Ray3 instantiaion, with origin and direction vector', function() {
			var pO = mLIB.Point3(0, 0, 0),
				dir = mLIB.Vec3(1, 1, 1),
				line = mLIB.Ray3(pO, dir);
		
            expect(line).to.be.equal({origin:{x:0.0, y:0.0, z:0.0}, direction: dir.Normalise()});
        });

        testFor('Ray3 instantiaion, with segment', function() {
			var seg = mLIB.Lin3Seg( mLIB.Point3(5, 23, 47), mLIB.Vec3(12, 43, 30) ),
				ray = mLIB.Ray3(seg);
		
            expect(ray).to.be.equal({origin:{x:5, y:23, z:47}, direction: seg.direction.normalise()});
        });

        testFor('Ray3 instantiaion, with line', function() {
			var line = mLIB.Lin3Seg( mLIB.Point3(5, 23, 47), mLIB.Vec3(12, 43, 30) ),
				ray = mLIB.Ray3(line);
		
            expect(ray).to.be.equal({origin:{x:5, y:23, z:47}, direction: line.direction.normalise()});
        });

        testFor('Ray3 instantiaion, defaults', function() {
			var ray = mLIB.Ray3();
		
            expect(ray).to.be.equal({origin: mLIB.Vec3.origin, direction: mLIB.Vec3.xAxis});
        });
	});  // performTestSuite()

}; //testSuiteRay3Instantion()

function testSuiteRay3() {
	performTestSuite('mLIB.Ray3 Integration', function() {

        testFor('Ray3 closest point on ray to a given point Q', function() {
			var P = mLIB.Point3(4, 4, 4),
				ray = mLIB.Ray3( P, mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				point, n;

			// test 1
			expect(ray.ClosestPoint(Q1)).to.be.equal(P);
			
			// test 2
			n = ray.ClosestPoint(Q2).minus(Q2);
			expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

			// test 3
			n = ray.ClosestPoint(Q3).minus(Q3);
			expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();

		});

        testFor('Ray3 distanceSquared between ray and a given point Q', function() {
			var P = mLIB.Point3(4, 4, 4),
				ray = mLIB.Ray3( P, mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				point, n, delta;

			// test 1
			delta = ray.distanceSquared(Q1) - P.minus(Q1).lengthSquared();
			expect(mLIB.Includes.isZero(delta)).to.be.ok();
			
			// test 2
			n = ray.ClosestPoint(Q2).minus(Q2);
            delta = ray.distanceSquared(Q2) - n.lengthSquared();
			expect(mLIB.Includes.isZero(delta)).to.be.ok();

			// test 3
			n = ray.ClosestPoint(Q3).minus(Q3);
            delta = ray.distanceSquared(Q3) - n.lengthSquared();
			expect(mLIB.Includes.isZero(delta)).to.be.ok();
		});

        testFor('Ray3 closest points between 2 rays', function() {
			var ray1 = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray

				ray2,
				points,
				n;
                
            // test 0 parallell rays
            ray2 = mLIB.Ray3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = ray1.ClosestPoints(ray2);
            n = points.Pc.Minus(points.Qc);
            expect(mLIB.Includes.isZero(n.dot(ray1.direction))).to.be.ok();
            expect(mLIB.Includes.isZero(n.dot(ray2.direction))).to.be.ok();
            
            // test 1
            ray2 = mLIB.Ray3( Q1, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(ray1.direction))) {
                expect(points.Pc).to.equal(ray1.origin);
            }
            
            if ( !mLIB.Includes.isZero(n.dot(ray2.direction)) ) {
                expect(points.Qc).equal(ray2.origin);
            }

            // test 2
            ray2 = mLIB.Ray3( Q2, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(ray1.direction))) {
                expect(points.Pc).to.equal(ray1.origin);
            }
            
            if ( !mLIB.Includes.isZero(n.dot(ray2.direction)) ) {
                expect(points.Qc).equal(ray2.origin);
            }

            // test 3
            ray2 = mLIB.Ray3( Q3, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(ray1.direction))) {
                expect(points.Pc).to.equal(ray1.origin);
            }
            
            if ( !mLIB.Includes.isZero(n.dot(ray2.direction)) ) {
                expect(points.Qc).equal(ray2.origin);
            }
        });
        
        testFor('Ray3 distanceSquared between 2 rays', function() {
			var ray1 = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				ray2,
				points,
				nsq, dsq;
                
            // test 0 parallel rays
            ray2 = mLIB.Ray3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = ray1.ClosestPoints(ray2);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray1.distanceSquared(ray2);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();
            
            // test 1
            ray2 = mLIB.Ray3( Q1, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray1.distanceSquared(ray2);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 2
            ray2 = mLIB.Ray3( Q2, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray1.distanceSquared(ray2);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 3
            ray2 = mLIB.Ray3( Q3, mLIB.Vec3(12, -43, -30) );
            points = ray1.ClosestPoints(ray2);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray1.distanceSquared(ray2);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

        });

        testFor('Ray3 closest points between a ray and a line', function() {
			var ray = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray

				line,
				points,
				n;

            // test 0 parallell rays
            line = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = ray.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);       
            expect(mLIB.Includes.isZero(n.dot(ray.direction))).to.be.ok();
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 1
            line = mLIB.Lin3( Q1, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(ray.direction))) {
                expect(points.Pc).to.equal(ray.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 2
            line = mLIB.Lin3( Q2, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            if ( !mLIB.Includes.isZero(n.dot(ray.direction))) {
                expect(points.Pc).to.equal(ray.origin);
            }
            
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

            // test 3
            line = mLIB.Lin3( Q3, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            n = points.Pc.Minus(points.Qc);
            
            if ( !mLIB.Includes.isZero(n.dot(ray.direction))) {
                expect(points.Pc).to.equal(ray.origin);
            }
            expect(mLIB.Includes.isZero(n.dot(line.direction))).to.be.ok();

        });

        testFor('Ray3 distanceSquared between a ray and a line', function() {
			var ray = mLIB.Ray3( mLIB.Point3(4, 4, 4), mLIB.Vec3(12, 43, 30) ),
				Q1 = mLIB.Point3(2, 2, 2),		// test 1: Q is before the ray origin, no normal
				Q2 = mLIB.Point3(8, -5, 22),	// test 2: normal between ray and Q exists, Q above ray
				Q3 = mLIB.Point3(8, 4, 3),	// test 3: normal between ray and Q exists, Q below ray
				ray2,
				points,
				nsq, dsq;
                
            // test 0 parallel rays
            line = mLIB.Lin3( mLIB.Point3(22, 12, 47), mLIB.Vec3(12, 43, 30) );
            points = ray.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();
            
            // test 1
            line = mLIB.Ray3( Q1, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 2
            line = mLIB.Ray3( Q2, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

            // test 3
            line = mLIB.Ray3( Q3, mLIB.Vec3(12, -43, -30) );
            points = ray.ClosestPoints(line);
            nsq = points.Pc.Minus(points.Qc).lengthSquared();
            dsq = ray.distanceSquared(line);
            expect(mLIB.Includes.isZero(nsq-dsq)).to.be.ok();

        });

	}); // performTestSuite()

}; //testSuiteRay3()


//----------------------------------------------------------------------

function testSuiteLin3SegPart2() {
	// testing agaianst lines
	performTestSuite('mLIB.Lin3Seg PART2', function() {		
		testFor('distanceSquared between segment and Line', function() {
			var pO = mLIB.Point3(3, 0, 0),
				dir0 = mLIB.Vec3(5, 0, 0), seg = mLIB.Lin3Seg(pO, dir0),
				
				qO = mLIB.Point3(1,2,0),
				dir1 = mLIB.Vec3(1,0,0), line = mLIB.Lin3(qO, dir1);
							
			expect(seg.distanceSquared(line)).to.be.eq(4);
		
		});

		/*testFor('distanceSquared between segment and Ray', function() {
			var pO = mLIB.Point3(3, 0, 0),
				dir0 = mLIB.Vec3(5, 0, 0), seg = mLIB.Lin3Seg(pO, dir0),
				
				qO = mLIB.Point3(1,2,0),
				dir1 = mLIB.Vec3(1,0,0), line = mLIB.Lin3(qO, dir1);
							
			expect(seg.distanceSquared(line)).to.be.eq(4);
		
		});*/

		
		testFor('ClosestPoints between segment and Line', function() {
			var pO = mLIB.Point3(3, 23,46.2),
				dir0 = mLIB.Vec3(5, 12, 33), seg = mLIB.Lin3Seg(pO, dir0),
				
				qO = mLIB.Point3(1,2,100),
				dir1 = mLIB.Vec3(11.3,4.8,15), line = mLIB.Lin3(qO, dir1)
				
				dsq = seg.distanceSquared(line),
				points = seg.ClosestPoints(line),
				ncsq = points.Pc.Minus(points.Qc).lengthSquared();

				expect(mLIB.Includes.isZero( dsq - ncsq )).to.be.ok();
		
		});	
		
	});  //performTestSuite

}; //testSuiteLin3SegPart2
//----------------------------------------------------------------------

function testSuiteVec3() {
    performTestSuite('Inital tests for mLIB.Vec3', function() {
        testFor('Vec3 instantiaion', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0);
            expect(v).to.be.equal({x:3.0, y:4.0, z:5.0});
        });

        testFor('Same object being referenced 1 - u sameAs v', function() {
            var u = v = mLIB.Vec3(3.0, 4.0, 5.0);
            expect(u).to.be.sameAs(v);
        });
        
        testFor('Same object being referenced 2 - u !sameAs v', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
                v = mLIB.Vec3(3.0, 4.0, 5.0);

            expect(u).not.to.be.sameAs(v);
        });

        testFor('Method: Length of v', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0),
                vdotv = 3.0*3.0 + 4.0*4.0 + 5.0*5.0;
                
            expect(v.length()).to.eq(sqrt(vdotv));
        });
        testFor('Method: LengthSquared - Scalar product v.v', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0),
                vdotv = 3.0*3.0 + 4.0*4.0 + 5.0*5.0;

            expect(v.lengthSquared()).to.eq(vdotv);
        });

        testFor('Method: Vector equality equalTo - u = v', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
                v = mLIB.Vec3(3.0, 4.0, 5.0);
                
            expect(u.equalTo(v)).to.be.ok();
        });
        
        testFor('Method: Vector equality notEqualTo - u != v', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
                v = mLIB.Vec3(-3.0, 4.0, 5.0);
                
            expect(u.notEqualTo(v)).to.be.ok();
        });
        testFor('Method: isZero vector - v != (0,0)', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0);

            expect(v.isZero()).to.not.be.ok();
        });
        
        testFor('Method: iZero vector - z', function() {
            var z = mLIB.Vec2(1.0e-7, -1.0e-7);

            expect(z.isZero()).to.be.ok();
        });

        testFor('Method: unit vector - v', function() {
            var v = mLIB.Vec3(1.0, 0.0, 0.0);

            expect(v.isUnit()).to.be.ok();
        });
        
        testFor('Method: 1. roundToZero v, if v is near zero', function() {
			var v = mLIB.Vec3(3.0, 4.0, 5.0);
			
            expect(!v.roundToZero().isZero()).to.be.ok();
        });    
        
        testFor('Method: 2 roundToZero z, if z is near zero', function() {
			var z = mLIB.Vec3(1.0e-7, -1.0e-7, 1.0e-7);
			
            expect(z.roundToZero()).to.be.equal({x:0.0, y:0.0, z:0.0});
        });    

        testFor('Method: minus - roundToZero z and minus vector (23.0, 0.0, 0,0)', function() {
			var z = mLIB.Vec3(23.0, -1.0e-7, 1.0e-7);

            expect(z.roundToZero().minus({x:23.0, y:0.0, z:0.0}).isZero()).to.be.ok();
        });

        testFor('Method: normalise and isUnit v', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0);

            expect(v.normalise().isUnit()).to.be.ok();
        });

        testFor('Method: isClass v instance of mLIB.Vec3 ', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0);

            expect(v.isClass(mLIB.Vec3)).to.be.ok();
        });
		
        testFor('Method: getClass of v ', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0);

            expect(v.getClass()).to.be(mLIB.Vec3);
        });
        
        testFor('Method: plus - v = u+v ', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
				v = mLIB.Vec3(-3.0, 4.0, 3.0 );
            v.plus(u);         // (-3.0, 4.0, 3.0 ) + (3.0, 4.0, 5.0)
            
            expect(v).to.be.equal({x:0.0, y:8.0, z:8.0});
        });

        testFor('Method: Plus - w = u+v ', function() {
            var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
				v = mLIB.Vec3(1.0, 2.0, 3.0),
				w = u.Plus(v);      // (-3.0, 4.0, 5.0 ) + (1.0, 2.0, 3.0)
				
            expect(w).to.be.equal({x:-2.0, y:6.0, z:8.0}).and.not.be.sameAs(u);
        });
        
        testFor('Method: minus - u = u-v ', function() {
            var u = mLIB.Vec3(-3.0, 4.0, 5.0 )
				v = mLIB.Vec3(1.0, 2.0, 3.0);
            u.minus(v);      // (-3.0, 4.0, 5.0 ) - (1.0, 2.0, 3.0)
            
            expect(u).to.be.equal({x:-4.0, y:2.0, z:2.0});
        });
        
        testFor('Method: Minus - w = u-v ', function() {
            var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
				v = mLIB.Vec3(1.0, 2.0, 3.0),
				w = u.Minus(v);      // (-3.0, 4.0, 5.0 ) - (1.0, 2.0, 3.0)
                
            expect(w).to.be.equal({x:-4.0, y:2.0, z:2.0}).and.not.be.sameAs(u);
        });
        
        testFor('Method: Negation - v = -u', function() {
            var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
                v = u.Negation();      // (3.0, -4.0, -5.0 )
                
            expect(v).to.be.equal({x:3.0, y:-4.0, z:-5.0}).and.not.be.sameAs(u);
        });

        testFor('Method: scaleUp - v = 2*v', function() {
            var v = mLIB.Vec3(1.0, 2.0, 3.0);
            v.scaleUp(2.0);         // (2.0, 4.0, 6.0 )
                
            expect(v).to.be.equal({x:2.0, y:4.0, z:6.0});
        });
        
        testFor('Method: ScaleUp - v = 2*u', function() {
            var u = mLIB.Vec3(1.0, 2.0, 3.0),
                v = u.ScaleUp(2.0);         // (2.0, 4.0, 6.0 )
                
            expect(v).to.be.equal({x:2.0, y:4.0, z:6.0}).and.not.be.sameAs(u);
        });

        testFor('Method: scaleDown - v = v/2', function() {
            var v = mLIB.Vec3(1.0, 2.0, 3.0);
            v.scaleDown(2.0);         // (0.5, 1.0, 1.5 )

            expect(v).to.be.equal({x:0.5, y:1.0, z:1.5});
        });

        testFor('Method: ScaleDown - v = u/2', function() {
            var u = mLIB.Vec3(1.0, 2.0, 3.0);
            	v = u.ScaleDown(2.0);         // (0.5, 1.0, 1.5 )
                
            expect(v).to.be.equal({x:0.5, y:1.0, z:1.5}).and.not.be.sameAs(u);
        });
		
        testFor('Method: dot product - v.w', function() {
			var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
				v = mLIB.Vec3(1.0, 2.0, 3.0);
               
            expect(u.dot(v)).to.be.equal(20.0);
        });
		
        testFor('Method: Cross -  u x v', function() {
			var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
				v = mLIB.Vec3(1.0, 2.0, 3.0);

            expect(u.Cross(v)).to.be.equal(cross(u,v));
        });
		
        testFor('Method: copy - u', function() {
			var u = mLIB.Vec3(-3.0, 4.0, 5.0 ),
                dummy = mLIB.Vec3(-33.0, 22.0, 12.0 );
                
            expect(u.copy(dummy)).to.be.equal({x:-33.0, y:22.0, z:12.0}).and.be.sameAs(u);
        });
	});

    performTestSuite('Testing Vec3 friend methods', function() {

        testFor('Friend: Copy - v', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0);
                w = mLIB.Vec3.Copy(v);
                
            expect(w).to.be.equal({x:3.0, y:4.0, z:5.0}).and.not.be.sameAs(v);
        });

        testFor('Friend: distance between 2 points p0, p1', function() {
            var p0 = mLIB.Point3(1.0, 2.0, 3.0 ),
                p1 = mLIB.Point3(2.0, 3.0, 4.0);
                
            expect(mLIB.Vec3.distance(p0, p1)).to.be.equal(distance(p0, p1));
        });

        testFor('Friend: distanceSquared between 2 points p0, p1', function() {
            var p0 = mLIB.Point3(1.0, 2.0, 3.0 ),
                p1 = mLIB.Point3(2.0, 3.0, 4.0);

	    function distanceSqrd(p0, p1) {
			var dx = p0.x-p1.x,
				dy = p0.y-p1.y,
				dz = p0.z-p1.z;
			return dx*dx + dy*dy + dz*dz;
	    }
                
            expect(mLIB.Vec3.distanceSquared(p0, p1)).to.be.equal(distanceSqrd(p0, p1));
        });

        testFor('Friend: ScaleUp - w = 2*v', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0),
                w = mLIB.Vec3.ScaleUp(2.0, v);
                
            expect(w).to.be.equal({x:6.0, y:8.0, z:10.0}).and.not.be.sameAs(v);
        });

        testFor('Friend: ScaleDown - w = v/2', function() {
            var v = mLIB.Vec3(3.0, 4.0, 5.0),
                w = mLIB.Vec3.ScaleDown(2.0, v);
                
            expect(w).to.be.equal({x:1.5, y:2.0, z:2.5}).and.not.be.sameAs(v);
        });

        testFor('Friend: Plus 2 vectors u+v', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
				v = mLIB.Vec3(2.0, 3.0, 4.0);

            expect(mLIB.Vec3.Plus(u, v)).to.be.equal({x:5.0, y:7.0, z:9.0});
        });

        testFor('Friend: Minus 2 vectors u-v', function() {
            var u = mLIB.Vec3(3.0, 4.0, 5.0),
				v = mLIB.Vec3(2.0, 3.0, 4.0);
                
            expect(mLIB.Vec3.Minus(u, v)).to.be.equal({x:1.0, y:1.0, z:1.0});
        });

        testFor('Friend: dot product 2 vectors u.v', function() {
              var u = mLIB.Vec3(3.0, 4.0, 5.0),
				  v = mLIB.Vec3(2.0, 3.0, 4.0);

			function dotProd(u, v) {
				return u.x*v.x + u.y*v.y + u.z*v.z;
			}
		
			expect(mLIB.Vec3.dot(u, v)).to.be.eq(dotProd(u, v));
        });

        testFor('Friend: Cross product 2 vectors w = u x v', function() {
			var u = mLIB.Vec3(3.0, 4.0, 5.0),
				v = mLIB.Vec3(2.0, 3.0, 4.0);
                
            expect(mLIB.Vec3.Cross(u, v)).to.be.equal(cross(u, v));
        });    
    }); // end of performTestSuite

};  // func Vec3TestSuite
//----------------------------------------------------------------------

function testSuiteVec2() {
    performTestSuite('Initial tests for mLIB.Vec2', function() {
        var v = u = mLIB.Vec2(3.0, 4.0), vdotv = v.x*v.x + v.y*v.y, 
            v2 = mLIB.Vec2(3.0, 4.0),
            w = new mLIB.Vec2(-3.0, 4.0),
            x = mLIB.Vec2(1.0, 2.0),
            z1 = mLIB.Vec2(1.0e-7, -1.0e-7),
            z2 = mLIB.Vec2(23.0, -1.0e-7),
            unit = mLIB.Vec2(1.0, 0.0);
                        
        testFor('Vec2 instantiaion', function() {
            expect(v).to.be.equal({x:3.0, y:4.0});
        });
        testFor('Same object being referenced 1 - u sameAs v', function() {
            expect(u).to.be.sameAs(v);
        });
        testFor('Same object being referenced 2 - v !sameAs v2', function() {
            expect(v).not.to.be.sameAs(v2);
        });
        testFor('Method: Length of v', function() {
            expect(v.length()).to.eq(sqrt(vdotv));
        });
        testFor('Method: LengthSquared - Scalar product v.v', function() {
            expect(v.lengthSquared()).to.eq(vdotv);
        });

        testFor('Method: Vector equality equalTo - v = v2', function() {
            expect(v.equalTo(v2)).to.be.ok();
        });
        
        testFor('Method: Vector equality notEqualTo - v != w', function() {
            expect(v.notEqualTo(w)).to.be.ok();
        });
        testFor('Method: isZero vector - v != (0,0)', function() {
            expect(v.isZero()).to.not.be.ok();
        });
        
        testFor('Method: near Zero vector - z1', function() {
            expect(z1.isZero()).to.be.ok();
        });

        testFor('Method: unit vector - unit', function() {
            expect(unit.isUnit()).to.be.ok();
        });
        
        testFor('Method: 1. roundToZero w, if w is near zero', function() {
            expect(!w.roundToZero().isZero()).to.be.ok();
        });    
        
        testFor('Method: 2 roundToZero z1, if z1 is near zero', function() {
            expect(z1.roundToZero()).to.be.equal({x:0.0, y:0.0});
        });    

        testFor('Method: minus - roundToZero z2 and minus vector (23.0, 0.0)', function() {
            expect(z2.roundToZero().minus({x:23.0, y:0.0}).isZero()).to.be.ok();
        });

        testFor('Method: normalise and isUnit v', function() {
            expect(v.normalise().isUnit()).to.be.ok();
        });

        testFor('Method: isClass v instance of mLIB.Vec2 ', function() {
            expect(v.isClass(mLIB.Vec2)).to.be.ok();
        });
        testFor('Method: getClass of v ', function() {
            expect(v.getClass()).to.be(mLIB.Vec2);
        });
        
        testFor('Method: plus - w = w+x ', function() {
            var w = mLIB.Vec2(-3.0, 4.0 );
            w.plus(x);         // (-3.0, 4.0 ) + (1.0, 2.0)
            
            expect(w).to.be.equal({x:-2.0, y:6.0});
        });

        testFor('Method: Plus - n = w+x ', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                n = w.Plus(x);      // (-3.0, 4.0 ) + (1.0, 2.0)
            expect(n).to.be.equal({x:-2.0, y:6.0}).and.not.be.sameAs(w);
        });
        
        testFor('Method: minus - w = w-x ', function() {
            var w = mLIB.Vec2(-3.0, 4.0 );
            w.minus(x);      // (-3.0, 4.0 ) - (1.0, 2.0)
            
            expect(w).to.be.equal({x:-4.0, y:2.0});
        });
        
        testFor('Method: Minus - n = w-x ', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                n = w.Minus(x);         // (-3.0, 4.0 ) - (1.0, 2.0)
                
            expect(n).to.be.equal({x:-4.0, y:2.0}).and.not.be.sameAs(w);
        });
        
        testFor('Method: Negation - w = -w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                n = w.Negation();      // (+3.0, -4.0 )
                
            expect(n).to.be.equal({x:3.0, y:-4.0}).and.not.be.sameAs(w);
        });

        testFor('Method: scaleUp - w = 2*w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 );
            w.scaleUp(2.0);         // (-6.0, 8.0 )
                
            expect(w).to.be.equal({x:-6.0, y:8.0});
        });
        
        testFor('Method: ScaleUp - n = 2*w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                n = w.ScaleUp(2.0);         // (-6.0, 8.0 )
                
            expect(n).to.be.equal({x:-6.0, y:8.0}).and.not.be.sameAs(w);
        });

        testFor('Method: scaleDown - w = 0.5*w', function() {
            var w = mLIB.Vec2(-6.0, 4.0 );
            w.scaleDown(2.0);         // (-3.0, 2.0 )
                
            expect(w).to.be.equal({x:-3.0, y:2.0});
        });

        testFor('Method: ScaleDown - w = 0.5*w', function() {
            var w = mLIB.Vec2(-6.0, 4.0 ),
                n = w.ScaleDown(2.0);         // (-3.0, 2.0 )
                
            expect(n).to.be.equal({x:-3.0, y:2.0}).and.not.be.sameAs(w);
        });
        
        testFor('Method: dot product - v.w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                v = mLIB.Vec2(2.0, 5.0);
                
            expect(w.dot(v)).to.be.equal(14.0);
        });
        
        testFor('Method: Perp - v = wperp', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                v = w.Perp();   // (-4.0, -3.0)
                
            expect(v).to.be.equal({x:-4.0, y:-3.0});
        });

        testFor('Method: perpDot - w.wperp = 0', function() {
            var w = mLIB.Vec2(-3.0, 4.0 );      // wperp = (-4.0, -30.)
                
            expect(w.perpDot(w)).to.be.equal(0);
        });


        testFor('Method: Copy - w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                dummy = mLIB.Vec2(-33.0, 22.0 );
                
            expect(w.copy(dummy)).to.be.equal({x:-33.0, y:22.0}).and.be.sameAs(w);
        });

    });


    performTestSuite('Testing Vec2 friend methods', function() {

        testFor('Friend: copyOf - w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                dummy = mLIB.Vec2.Copy(w);
                
            expect(dummy).to.be.equal({x:-3.0, y:4.0}).and.not.be.sameAs(w);
        });

        testFor('Friend: distance between 2 points p0, p1', function() {
            var p0 = mLIB.Point2(1.0, 4.0 ),
                p1 = mLIB.Point2(2.0, 5.0 );
                
            expect(mLIB.Vec2.distance(p0, p1)).to.be.equal(sqrt(2));
        });

        testFor('Friend: distanceSquared between 2 points p0, p1', function() {
            var p0 = mLIB.Point2(1.0, 4.0 ),
                p1 = mLIB.Point2(2.0, 5.0 );
                
            expect(mLIB.Vec2.distanceSquared(p0, p1)).to.be.equal(2);
        });

        testFor('Friend: ScaleUp - w = 2*w', function() {
            var w = mLIB.Vec2(-3.0, 4.0 ),
                dummy = mLIB.Vec2.ScaleUp(2.0, w);         // (-6.0, 8.0 )
                
            expect(dummy).to.be.equal({x:-6.0, y:8.0}).and.not.be.sameAs(w);
        });

        testFor('Friend: ScaleDown - w = 2*w', function() {
            var w = mLIB.Vec2(-4.0, 6.0 ),
                dummy = mLIB.Vec2.ScaleDown(2.0, w);         // (-2.0, 3.0 )
                
            expect(dummy).to.be.equal({x:-2.0, y:3.0}).and.not.be.sameAs(w);
        });

        testFor('Friend: add 2 vectors v0+v1', function() {
            var v0 = mLIB.Vec2(1.0, 4.0 ),
                v1 = mLIB.Vec2(2.0, 5.0 );
                
            expect(mLIB.Vec2.Plus(v0, v1)).to.be.equal({x:3.0, y:9.0});
        });

        testFor('Friend: subtract 2 vectors v0-v1', function() {
            var v0 = mLIB.Vec2(1.0, 4.0 ),
                v1 = mLIB.Vec2(2.0, 5.0 );
                
            expect(mLIB.Vec2.Minus(v0, v1)).to.be.equal({x:-1.0, y:-1.0});
        });

        testFor('Friend: dot product 2 vectors v0.v1', function() {
            var v0 = mLIB.Vec2(1.0, 4.0 ),
                v1 = mLIB.Vec2(2.0, 5.0 );
                
            expect(mLIB.Vec2.dot(v0, v1)).to.be.equal(22.0);
        });
    });
	
}   // testSuiteVec2
//----------------------------------------------------------------------
